package logger

import (
	"bytes"
	"encoding/json"

	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"gopkg.in/natefinch/lumberjack.v2"
)

type MonitorMetric string

const (
	MonitorHttpRequest MonitorMetric = "http_request"
	MonitorHttpCall    MonitorMetric = "http_call"
)

var MonitorCode = map[MonitorMetric]string{
	MonitorHttpRequest: "93010001",
	MonitorHttpCall:    "93010002",
}

// 用于appmonitor 调试
// [2021-12-10 15:08:40.523][decs-cadm][2835][{"CODE":"5500008","COST_TIME":"43","RES_CODE":"0"}][Monitor_getVDIOrderList]
type MonitorFormatter struct{}

func (s *MonitorFormatter) Format(entry *logrus.Entry) ([]byte, error) {

	b := &bytes.Buffer{}
	timestamp := entry.Time.Local().Format("2006-01-02 15:04:05.0000")

	// 时间
	s.writeField(b, timestamp)
	// 子系统名
	s.writeField(b, viper.GetString("ModuleName"))
	// 子系统Id
	s.writeField(b, viper.GetString("SubSystemId"))
	// JSON部分

	data := make(logrus.Fields)
	for k, v := range entry.Data {
		data[k] = v
	}

	// 三个必填项目 code ，cost_time ，res_code
	if _, ok := data["cost_time"]; !ok {
		data["cost_time"] = "0"
	}
	if _, ok := data["res_code"]; !ok {
		data["res_code"] = "0"
	}

	mCode, ok := MonitorCode[MonitorMetric(entry.Message)]
	if ok {
		data["code"] = mCode
	} else {
		data["code"] = "0"
	}

	info, _ := json.Marshal(data)
	s.writeField(b, string(info))
	s.writeField(b, entry.Message)
	b.WriteString("\n")
	return b.Bytes(), nil
}

func (s *MonitorFormatter) writeField(b *bytes.Buffer, val string) {
	b.WriteByte('[')
	b.WriteString(val)
	b.WriteByte(']')
}

var (
	// std is the name of the standard logger in stdlib `log`
	monitor Monitor
)

type Monitor struct {
	logrus.Logger
}

func InitMonitor(l *lumberjack.Logger) Monitor {
	monitor = NewMonitor(l)
	return monitor
}

func NewMonitor(l *lumberjack.Logger) Monitor {
	logger := logrus.New()
	logger.SetLevel(logrus.InfoLevel)
	logger.Formatter = &MonitorFormatter{}

	logger.SetOutput(l)

	m := Monitor{}
	m.Logger = *logger
	return m
}

func ReportMonitor(fields logrus.Fields, metric MonitorMetric) {
	monitor.WithFields(fields).Info(string(metric))
}

func AddMonitorHook(hook logrus.Hook) {
	monitor.AddHook(hook)
}
